服務上k8s之後, 除了介接的系統會來打API之外, 在API沒有被觸發的期間如果服務發生問題, 很難被事先發現。
以我們現行的專案來說, k8s上面起的服務有50-60個, 每個服務還有多個pod, 況且每個服務的使用頻率不一樣, 有的每秒會被打幾百次, 發生問題很快就能被發現, 有的一兩個月都不會被打到一次, 等到要用的時候才發現它壞掉, 那就是一陣手忙腳亂了。
所以提供一個health check
讓k8s可以幫忙關懷各個服務的狀況, 確實掌握叢集中裡面大大小小, 從熱門到邊緣的服務狀況, 主動發現問題才能在問題引爆之前多爭取一點時間。
這邊採用的方式是在服務上開啟一個http port, 讓k8s定期發送一個 HTTP request給Pod。
cmd.go 加上啟動 http port function runHealth()
, 在每次k8s詢問的時候打一次自身的Ping並回應服務執行時長。
grpcServer := runGrpcServer(shutdownObserver, sv)
// health check
go runHealth()
........ (省略)
func runHealth() {
started := time.Now()
connSelf, err := grpc.Dial("localhost"+config.Listen, grpc.WithInsecure())
if err != nil {
Logger.WithFields(map[string]interface{}{"error": err}).Errorf("連線失敗")
}
coco := coconut.NewCoconutClient(connSelf)
Logger.Debugf("Healthy API is Running at port: %s", config.HealthPort)
http.HandleFunc(config.HealthPath, func(w http.ResponseWriter, r *http.Request) {
// 確認gRPC服務有通
ping, err := coco.Ping(context.Background(), &coconut.PingRequest{})
if err == nil {
w.WriteHeader(200)
data := fmt.Sprintf("Already run: %v", time.Since(started))
if _, errw := w.Write([]byte(data)); errw != nil {
Logger.WithFields(map[string]interface{}{"error": errw}).Errorf("runHealth")
}
} else {
w.WriteHeader(500)
Logger.Errorf("Service Not Ready yet, ping: %#v, err: %s", ping, err.Error())
}
})
logrus.Fatal(http.ListenAndServe(":"+config.HealthPort, nil))
}
在去年的k8s主題中有學到 kubelet 可以執行三種探測
health check 這裡採用的是 readinessProbe
, 主要用來判斷Pod是否可以接收request。所以會在部署k8s deployment.yaml時加上這段
template:
spec:
containers:
readinessProbe:
httpGet:
path: {{ health.Path }}
port: {{ health.httpPort }}
initialDelaySeconds: 2
periodSeconds: 5
參考資料